/**
 * @file filter
 * @author liaoxuezhi@baidu.com
 */
/* eslint fecs-indent: [0, "space", 2, 2] */

import * as React from 'react';
import {
  PureComponent,
  PropTypes
} from 'react';
import * as cx from 'classnames';
import * as assign from 'object-assign';
import './tree.scss';
import {Enhance as FormItem} from '../form-item';
import {map as mapTree} from '../../../util/tree';
import {Collapse} from 'react-bootstrap';
import {findIndexByAttrValue} from '../../../util/helper';

@FormItem({
  type: 'tree',
  validateSchema: 'http://amis.baidu.com/schemas/form.json#/definitions/formItem'
})
export default class TreeInput extends PureComponent {
  static propTypes = {
    // 请参考 schema 定义
    // className: PropTypes.string,
    // source: PropTypes.string,
    // options: PropTypes.arrayOf(PropTypes.shape({
    //   label: PropTypes.string.isRequired,
    //   value: PropTypes.any.isRequired,
    //   children: PropTypes.array
    // }))
  };

  static defaultProps = {
    value: 0,
    wrapperClassName: '',
    placeholder: '暂无数据',
    rootLabel: '顶级',
    rootValue: 0,
    hideRoot: false,
    collapsable: true,
    showIcon: true,
    multiple: false,
    joinValues: true,
    delimiter: ',',
    animation: true,
    valueField: 'value'
  };

  constructor(props) {
    super(props);

    this.state = {
      options: props.sourceLocalData ? this.buildOptions(props, props.sourceLocalData) : this.buildOptions(props)
    }
  }

  componentWillReceiveProps(nextProps) {
    const props = this.props;

    if (props.sourceFetching && !nextProps.sourceFetching && nextProps.sourceData) {
      const sourceData = nextProps.sourceData;

      this.setState({
        options: this.buildOptions(nextProps, sourceData.options)
      });

      sourceData.hasOwnProperty('value') && this.props.setValue(sourceData.value);
    }

    if (props.sourceLocalData !== nextProps.sourceLocalData) {
      const sourceLocalData = nextProps.sourceLocalData;
      this.setState({
        options: this.buildOptions(nextProps, sourceLocalData)
      });
    }
  }

  buildOptions(props, options = props.options) {
    const hideRoot = props.hideRoot;
    const placeholder = props.placeholder;

    if (!options || !options.length) {
      options = [
        {
          label: placeholder,
          value: null,
          isPlaceHolder: true
        }
      ];
    }

    if (options && !hideRoot) {
      options = [
        {
          label: props.rootLabel,
          value: props.rootValue,
          isRoot: true,
          children: options
        }
      ];
    }

    return mapTree(options, item => assign({}, item, {
      value: String(item.value),
      collapsable: item.hasOwnProperty('collapsable') ? item.collapsable : props.collapsable,
      showIcon: item.hasOwnProperty('showIcon') ? item.showIcon : props.showIcon,
      collapsed: item.hasOwnProperty('collapsed') ? item.collapsed : false
    }));
  }

  handleChecked(option, checked) {
    const {
      joinValues,
      delimiter,
      getValue,
      valueField
    } = this.props;

    const value = getValue();
    const arr = typeof value === 'string' && joinValues ? value.split(delimiter) : (value && value.concat() || []);
    const pool = [option];

    while (pool.length) {
      let current = pool.shift();
      let newValue = joinValues ? String(current.value) : current;
      let idx = joinValues ? arr.indexOf(newValue) : findIndexByAttrValue(arr, valueField, newValue ? newValue[valueField] : null);

      if (!checked && ~idx) {
        arr.splice(idx, 1);
      } else if (checked && !~idx) {
        arr.push(newValue);
      }

      if (current.children) {
        pool.unshift.apply(pool, current.children);
      }
    }

    this.props.setValue(joinValues ? arr.join(delimiter) : arr);
  }

  reload() {
    const {
      source,
      fetchSource,
      data,
      setValue
    } = this.props;

    setValue(null);
    source && fetchSource(data);
  }

  toggleCollapsed(option) {
    const options = this.state.options;

    this.setState({
      options: mapTree(options, item => item.value === option.value ? assign({}, option, {
        collapsed: !item.collapsed
      }) : item)
    });
  }

  renderItem(option, key, level = 0, arr = null, value = '') {
    const disabled = option.isPlaceholder || option.disabled;
    let selected = value == option.value;

    const {
      multiple,
      joinValues,
      animation,
      valueField
    } = this.props;

    if (multiple) {
      let idx = joinValues ? arr.indexOf(option[valueField]) : findIndexByAttrValue(arr, valueField, option[valueField]);
      selected = ~idx;
    }

    return (
      <li
        key={key}
        className={cx({
          active: selected && !multiple
        })}
      >
        <a
          className={cx(option.className)}
        >
          {option.children && option.collapsable ? (
            <i onClick={this.toggleCollapsed.bind(this, option)} className={`collapse-icon fa ${option.collapsed ? 'fa-angle-right' : 'fa-angle-down'}`} />
          ) : null}
          {option.showIcon ? (
            <i className={`fa ${option.children ? 'fa-folder' : 'fa-file'}`} />
          ) : null}
          <span
            className={cx({
              disabled: disabled,
              "text-muted": disabled
            })}
            onClick={() => {
              if (multiple && !disabled) {
                this.handleChecked(option, !selected)
              } else {
                !disabled && !selected && this.props.setValue(joinValues ? option.value : option)
              }
            }}
          >
            {option.label}
          </span>
          {multiple && !option.isRoot && !disabled ? (
            <label className="i-checks i-checks-sm m-l-xs">
              <input type="checkbox" disabled={disabled} checked={selected} onChange={e => this.handleChecked(option, e.currentTarget.checked)} />
              <i />
            </label>) : null}
        </a>

        {option.children && (
          animation && option.collapsable ? (
            <Collapse in={!option.collapsed}>
              <ul>
                {option.children.map((option, key) => this.renderItem(option, key, level + 1, arr, value))}
              </ul>
            </Collapse>
          ) : (
            <ul className={option.collapsed ? 'hide' : ''}>
              {option.children.map((option, key) => this.renderItem(option, key, level + 1, arr, value))}
            </ul>
          )
        )}
      </li>
    )
  }

  renderInput() {
    const {
      options,
    } = this.state;
    const {
      inputClassName,
      joinValues,
      delimiter,
      getValue,
      rootValue
    } = this.props;

    const value = getValue() || rootValue;
    const arr = typeof value === 'string' && joinValues ? value.split(delimiter) : (value || []);


    return (
      <div className={cx('tree-view', inputClassName)}>
        <ul>
          {options.map((option, key) => this.renderItem(option, key, 0, arr, value))}
        </ul>
      </div>
    );
  }

  render() {
    return (
      <div key="input">{this.renderInput()}</div>
    );
  }
}